package com.github.zyf.ioaj.network.utils;

import java.text.DecimalFormat;

/**
 * @author zyf
 */
public class BPArrayUtils {

    private static final DecimalFormat df = new DecimalFormat("0.000000");
    private static final DecimalFormat df_print = new DecimalFormat("0.00");

    private BPArrayUtils(){}

    /**
     * 矩阵转置
     *
     * @param A 待转置矩阵
     * @return 转置好的矩阵（不修改原来的矩阵）
     */
    public static double[][] transpose(double[][] A) {
        int row = A.length;
        int column = A[0].length;
        double[][] AT = new double[column][row];
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < column; c++) {
                AT[c][r] = A[r][c];
            }
        }
        return AT;
    }

    /**
     * 寻找最值
     * type = true为最大值
     * type = false为最小值
     *
     * @param array 目标数组
     * @param type  最值类型
     * @return 最值
     */
    public static double findMaxOrMin(double[] array, boolean type) {
        // true为最大值 false为最小值
        return getMaxOrMin(array, type, 0, array.length - 1);
    }

    /**
     * 二分法递归实现最值寻找
     *
     * @param array 目标数组
     * @param type  最值类型
     * @param left  左端点
     * @param right 右端点
     * @return 最值
     */
    public static double getMaxOrMin(double[] array, boolean type, int left, int right) {
        double m;
        if (left > right - 2) {
            if (type) {
                m = Math.max(array[left], array[right]);
            } else {
                m = Math.min(array[left], array[right]);
            }
        } else {
            int mid = (left + right) / 2;
            double leftM = getMaxOrMin(array, type, left, mid);
            double rightM = getMaxOrMin(array, type, mid + 1, right);
            if (type) {
                m = Math.max(leftM, rightM);
            } else {
                m = Math.min(leftM, rightM);
            }
        }
        return m;
    }

    /**
     * 计算均值
     *
     * @param array 目标数组
     * @return 数组的均值
     */
    public static double calcMean(double[] array) {
        int n = array.length;
        double sum = 0.0;
        for (double v : array) {
            sum += v;
        }
        return sum / n;
    }

    /**
     * 计算方差
     *
     * @param array 目标数组
     * @return 数组方差
     */
    public static double calcVariance(double[] array) {
        int N = array.length - 1;
        double sum = 0.0;
        double mean = calcMean(array);
        for (double v : array) {
            sum += (v - mean) * (v - mean);
        }
        return sum / N;
    }

    /**
     * 计算标准差
     *
     * @param array 目标数组
     * @return 数组标准差
     */
    public static double calcStdDev(double[] array) {
        double variance = calcVariance(array);
        return Math.sqrt(variance);
    }

    /**
     * 标准化数据（不改变原数组数据，生成新数据）
     *
     * @param array 目标数组
     * @return double[]
     */
    public static double[] Standardization(double[] array) {
        double mean = calcMean(array);
        double stdDev = calcStdDev(array);
        double[] newArray = new double[array.length];
        for (int i = 0; i < array.length; i++) {
            newArray[i] = (array[i] - mean) / stdDev;
        }
        return newArray;
    }

    /**
     * 数组归一化（不改变原数组数据，生成新数据）
     *
     * @param array 目标数组
     * @return double[]
     */
    public static double[] Normalization(double[] array) {
        double max = findMaxOrMin(array, true);
        double min = findMaxOrMin(array, false);
        int n = array.length;
        double d = max - min;
        double[] newArray = new double[n];
        for (int i = 0; i < n; i++) {
            newArray[i] = (array[i] - min) / d;
        }
        return newArray;
    }

    /**
     * 矩阵归一化
     *
     * @param matrix 样本矩阵
     * @return 归一化后的矩阵
     */
    public static double[][] normalizeMatrix(double[][] matrix) {
        int r = matrix.length;
        int c = matrix[0].length;
        double[][] newMatrix = new double[r][c];
        for (int i = 0; i < matrix.length; i++) {
            newMatrix[i] = Normalization(matrix[i]);
        }
        return newMatrix;
    }

    /**
     * 矩阵标准化
     *
     * @param matrix 样本矩阵
     * @return 标准化后的矩阵
     */
    public static double[][] standardizeMatrix(double[][] matrix) {
        int r = matrix.length;
        int c = matrix[0].length;
        double[][] newMatrix = new double[r][c];
        for (int i = 0; i < matrix.length; i++) {
            newMatrix[i] = Standardization(matrix[i]);
        }
        return newMatrix;
    }

    /**
     * 将数组转化为字符串
     *
     * @param array 数组
     * @param label 标签
     * @return 解析字符串
     */
    public String parseArray(double[] array, String label) {
        StringBuilder output = new StringBuilder(label + " = { ");
        for (double v : array) {
            output.append(df.format(v)).append(" ");
        }
        output.append("}");
        return String.valueOf(output);
    }

    /**
     * 将二维数组转化为字符串
     *
     * @param array    二维数组
     * @param label    一级标签
     * @param subLabel 二级标签
     * @return 解析字符串
     */
    public static String parseArray(double[][] array, String label, String subLabel) {
        StringBuilder output = new StringBuilder(label + " = { ");
        int count = 1;
        for (double[] subArray : array) {
            output.append(subLabel).append("_").append(count).append(":{ ");
            for (double v : subArray) {
                output.append(df.format(v)).append(" ");
            }
            output.append("} ");
            count++;
        }
        output.append("}");
        return String.valueOf(output);
    }

    /**
     * 打印矩阵
     *
     * @param matrix 目标矩阵
     * @return 打印值
     */
    public static String printMatrix(double[][] matrix) {
        StringBuilder result = new StringBuilder();
        int r = matrix.length;
        int c = matrix[0].length;
        int i, j;
        for (i = 0; i < r; i++) {
            for (j = 0; j < c; j++) {
                result.append(df_print.format(matrix[i][j])).append(" ");
            }
            result.append("\n");
        }
        return String.valueOf(result);
    }
}
